package com.leyou.interceptor;

import com.leyou.auth.entity.UserInfo;
import com.leyou.auth.utils.JwtUtils;
import com.leyou.common.utils.CookieUtils;
import com.leyou.config.JwtProperties;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 编写SpringMVC拦截器
 * springmvc的过滤器就像以前的servlet的拦截器一样。这里需要HandlerInterceptor接口
 * 如果实现HandlerInterceptor接口的话，三个方法都必须实现(处理前，处理完，全部处理完成)，不管你需不需要
 * 此时spring提供了一个HandlerInterceptorAdapter适配器（种适配器设计模式的实现），允许我们只实现需要的回调方法。
 * <p>
 * 为什么用ThreadLocal:
 * 使用前提：类似于下面代码的情况，需要验证token获取UserInfo,来确定账户是否已经登录，如果只是定义一个userInfo变量，获取token再去
 * 解析赋值的话，会出现线程安全的问题，因为是同一个变量，多人访问无法确认是谁的userInfo，这样请求到达Controller后可以共享User
 * <p>
 * 引入ThreadLocal:
 * 当变量是同一个，但是每个线程所使用的值都不一样，这种情况之下ThreadLocal就可以使用，把数据放到ThreadLocal域里。
 * 除了咱们的例子外，还有数据库的连接，那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他，并且各自使用各自的。这种情况，ThreadLocal就比较好的解决了这个问题。
 */
@Component
@EnableConfigurationProperties(JwtProperties.class)
public class LoginInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private JwtProperties pro;

    // 定义一个Threadlocal线程域
    private static final ThreadLocal<UserInfo> T1 = new ThreadLocal<>();

    /**
     * pre 前置拦截器
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取token
        String token = CookieUtils.getCookieValue(request, "LY_TOKEN");
        if (StringUtils.isBlank(token)) {
            // 代表未登录,直接返回401
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }

        try {
            UserInfo user = JwtUtils.getInfoFromToken(token, this.pro.getPublicKey());
            // 解析的userInfo放入线程域里
            T1.set(user);
            return true;
        } catch (Exception e) {
            //如果抛出异常,证明未登录(拦截器可以通过抛出异常（或者返回false）来暂停request的执行)
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
    }

    /**
     * 后置拦截器
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 处理完删除线程域
        T1.remove();
    }

    // 对外提供了静态的方法：getLoginUser()来获取User信息
    public static UserInfo getUserInfo() {
        return T1.get();
    }
}
